/*
 * $Id: mtx.c,v 1.5 2005-12-28 14:46:55 bacon Exp $
 */

#include <xp/bas/mtx.h>
#include <xp/bas/memory.h>
#include <xp/bas/assert.h>
#include <xp/bas/errno.h>

#if defined(_WIN32)
	#include <process.h>
#else
	#if defined(AIX) && defined(__GNUC__)
		typedef int crid_t;
		typedef unsigned int class_id_t;
	#endif
	#include <pthread.h>
#endif

xp_mtx_t* xp_mtx_open (xp_mtx_t* mtx)
{
	if (mtx == XP_NULL) {
		mtx = (xp_mtx_t*) xp_malloc (xp_sizeof(xp_mtx_t));
		if (mtx == XP_NULL) return XP_NULL;
		mtx->__dynamic = xp_true;
	}
	else mtx->__dynamic = xp_false;

#ifdef _WIN32
	mtx->hnd = CreateMutex (XP_NULL, FALSE, XP_NULL);
	if (mtx->hnd == XP_NULL) {
		int num = xp_maperrno(GetLastError());
		if (mtx->__dynamic) xp_free (mtx);
		xp_seterrno (num);
		return XP_NULL;
	}
#else
	/*
	xp_ensure (pthread_mutexattr_init (&attr) == 0);
	if (pthread_mutexattr_settype (&attr, type) != 0) {
		int num = xp_geterrno();
		pthread_mutexattr_destroy (&attr);
		if (mtx->__dynamic) xp_free (mtx);
		xp_seterrno (num);
		return XP_NULL;
	}
	xp_ensure (pthread_mutex_init (&mtx->hnd, &attr) == 0);
	xp_ensure (pthread_mutexattr_destroy (&attr) == 0);
	*/
	xp_ensure (pthread_mutex_init (&mtx->hnd, XP_NULL) == 0);
#endif

	return mtx;
}

int xp_mtx_close (xp_mtx_t* mtx)
{
#ifdef _WIN32
	if (CloseHandle(mtx->hnd) == FALSE) {
		xp_seterrno (xp_maperrno(GetLastError()));
		return -1;
	}
#else
	if (pthread_mutex_destroy(&mtx->hnd) != 0) return -1;
#endif
	if (mtx->__dynamic) xp_free (mtx);
	return 0;
}

int xp_mtx_lock (xp_mtx_t* mtx)
{
#ifdef _WIN32
	/* 
	 * MSDN
	 *   WAIT_ABANDONED The specified object is a mutex object that was 
	 *                  not released by the thread that owned the mutex 
	 *                  object before the owning thread terminated. 
	 *                  Ownership of the mutex object is granted to the 
	 *                  calling thread, and the mutex is set to nonsignaled.
	 *   WAIT_OBJECT_0  The state of the specified object is signaled. 
	 *   WAIT_TIMEOUT   The time-out interval elapsed, and the object's 
	 *                  state is nonsignaled. 
	 *   WAIT_FAILED    An error occurred
	 */
	if (WaitForSingleObject (
		mtx->hnd, INFINITE) == WAIT_FAILED) {
		xp_seterrno (xp_maperrno(GetLastError()));
		return -1;
	}
#else
	if (pthread_mutex_lock (&mtx->hnd) != 0) return -1;
#endif
	return 0;
}

int xp_mtx_unlock (xp_mtx_t* mtx)
{
#ifdef _WIN32
	if (ReleaseMutex (mtx->hnd) == FALSE) {
		xp_seterrno (xp_maperrno(GetLastError()));
		return -1;
	}
#else
	if (pthread_mutex_unlock (&mtx->hnd) != 0) return -1;
#endif
	return 0;
}

int xp_mtx_trylock (xp_mtx_t* mtx)
{
#ifdef _WIN32
	if (WaitForSingleObject (mtx->hnd, 0) != WAIT_OBJECT_0) {
		xp_seterrno (xp_maperrno(GetLastError()));
		return -1;
	}
#else
	if (pthread_mutex_trylock (&mtx->hnd) != 0) return -1;
#endif
	return 0;
}
